#include"Task.hpp"
#include<iostream>
#include<unistd.h>
#include<string>
#include<vector>
#include<cstdlib>
#include<assert.h>
#include<ctime>
#include <sys/types.h>
#include <sys/wait.h>

const int processnum=5;
std::vector<task_t> tasks;    //里面放函数地址

//先描述
class channel
{
public:
    channel(int cmdfd,int slaverid,std::string& processname)
    :_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname)
    {}
public:
    int _cmdfd;    //管道的写端的文件描述符，方便父进程写入
    int _slaverid;       //子进程id
    std::string _processname;  //子进程名字,方便我们打印日志
};


//读
void slaver()
{
    //read(0);
    while(1)
    {
        int cmdcode=0;
        int n=read(0,&cmdcode,sizeof(cmdcode));  //以4字节为单位读数据,如果父进程不给子进程读数据，则子进程在此处阻塞
        if(n==sizeof(int))
        {
            //执行cmdcode对应的任务列表
            std::cout<<getpid()<<" :  cmdcode: "<<cmdcode<<std::endl;
            if(cmdcode>=0 && cmdcode<tasks.size())     tasks[cmdcode]();
        }
        if(n==0)  break;
    }
}

//输入：const &
//输出：*
//输入输出：&
void InitProcessPool(std::vector<channel> *channels)
{
    std::vector<pid_t> writefd; 
    for(int i=0;i<processnum;i++)   //父写子读
    {
        int pipefd[2];  //临时数组
        int n=pipe(pipefd);
        assert(!n);
        (void)n;

        pid_t id=fork();
        if(id==0)
        {
            //child
            close(pipefd[1]);
            //slaver(pipedf[0]);
            dup2(pipefd[0],0);  //以后子进程读都从0号文件符里读
            close(pipefd[0]);
            for(auto fd:writefd)
            {
                close(fd);
            }
            slaver();     //子进程一直在此等待，直到父进程写了数据到管道中
            std::cout<<"process"<<": "<<getpid()<<" quit "<<std::endl;
            exit(0);
        }
        //father-》写
        close(pipefd[0]);

        //管道完全创建好了，开始添加channel字段了
        std::string channelname="channel-"+std::to_string(i);
        channels->push_back(channel(pipefd[1],id,channelname));

        writefd.push_back(pipefd[1]);    //把父进程的文件写端记录下来，之后关闭子进程的不相关的文件的写端，
                                                                    //防止之后的子进程也打开其他文件的写端
    }

}

void Debug(const std::vector<channel> &channels)
{
     //打印看看
    for(const auto& iter:channels)
    {
       std::cout<<iter._cmdfd<<" "<<iter._slaverid<<" "<<iter._processname<<std::endl;
    }
}

void Menu()
{
    std::cout<<"1. 更新日志****************2. 更新野区*********"<<std::endl;
    std::cout<<"3. 检查软件是否更新*********4. 用户释放技能*********"<<std::endl;
    std::cout<<"0. 退出******************************************"<<std::endl;
}

void CtrlSlaver(const std::vector<channel> &channels)
{
    //父进程开始写数据
    int which=0;
    int cnt=5;
    int chioce=0; 
    while(cnt--) //随机发送100条数据
    {
        Menu();
        std::cin>>chioce;
        if(chioce==0)   break;

        //1. 选择数据，通过菜单选
        int cmdcode=chioce-1;
  
        //2. 选择子进程(用下标表示子进程)    子进程轮询
        //int processpos=rand()%channels.size();
        

        std::cout<<"father say:"<<" cmdcode: "<<cmdcode<<" already send to "<<channels[which]._slaverid<<", _processname: "
        <<channels[which]._processname <<std::endl;

        //3. 父进程写数据到管道---数据是函数地址在数组中的下标
        write(channels[which]._cmdfd,&cmdcode,sizeof(cmdcode));

        which++;
        which%=channels.size();
       
    }
}

void QuitChannel(const std::vector<channel> &channels)
{
    for(const auto& iter:channels)   
    {
        close(iter._cmdfd);   //把父进程打开的写端关了，之后文件会读完，n==0,退出读，子进程exit(0),退出
        waitpid(iter._slaverid,nullptr,0);
    }
}

int main()
{
    srand(time(nullptr)^getpid()^1023);   //种一个种子，异或是为了让数据更离散

    //任务
    LoadTask(&tasks);

    //管道
    std::vector<channel> channels;   //再组织
    //初始化管道
    InitProcessPool(&channels);  //子进程都在等着读文件
    Debug(channels);

    //开始控制子进程
    CtrlSlaver(channels);

    //处理结尾
    QuitChannel(channels);

    return 0;
 
}
